package org.zeapp.opencloud.framework.synchro;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.zeapp.opencloud.framework.cloud.Cloud;
import org.zeapp.opencloud.framework.cloud.CloudEntry;
import org.zeapp.opencloud.framework.cloud.CloudException;
import org.zeapp.opencloud.framework.crypto.AESCrypt;

/**
 * This utility class is used to retrieve remote FolderAlias and to synchronize
 * remote FolderAlias.
 * 
 * @author Amphiprion
 * 
 */
public class FolderAliasUtil {
	/**
	 * retrieve the folder aliases (root folder) already existing in the cloud.
	 * 
	 * @param cryptEngine
	 *            the crypting engine
	 * @param cloud
	 *            the cloud service to use
	 * @return the folder aliases or an empty list if no alias exists.
	 * @throws CloudException
	 *             if an error occurs
	 */
	public static List<FolderAlias> getFolderAliases(AESCrypt cryptEngine,
			Cloud cloud) throws CloudException {
		try {
			List<FolderAlias> folderAliases = new ArrayList<FolderAlias>();
			HashMap<String, CloudEntry> entriesByNames = new HashMap<String, CloudEntry>();
			List<CloudEntry> listing = cloud.browseDirectory("/");
			for (CloudEntry child : listing) {
				entriesByNames.put(child.getName(), child);
			}

			CloudEntry entry = entriesByNames.get("aliases");
			if (entry != null && entry.isFile()) {
				File tmpEncryptedFile = File
						.createTempFile("aliases", ".crypt");
				File tmpFile = File.createTempFile("aliases", ".tmp");
				FileOutputStream outputStream = new FileOutputStream(
						tmpEncryptedFile);
				// DbxEntry.File downloadedFile = client.getFile("/fmeo", null,
				// outputStream);
				cloud.getFile("/aliases", outputStream);
				outputStream.close();
				// decrypt tmpEncryptedFile
				cryptEngine.decrypt(tmpEncryptedFile.getAbsolutePath(),
						tmpFile.getAbsolutePath());
				tmpEncryptedFile.delete();
				//
				BufferedReader br = new BufferedReader(new InputStreamReader(
						new FileInputStream(tmpFile), "utf-8"));
				int index;
				for (String line = null; (line = br.readLine()) != null;) {
					index = line.indexOf('=');
					if (index != 1) {
						String uid = line.substring(0, index);
						CloudEntry folder = entriesByNames.get(uid);
						if (folder != null && folder.isFolder()) {
							String name = line.substring(index + 1);
							FolderAlias alias = new FolderAlias(uid, name);
							folderAliases.add(alias);
						}
					}
				}
				br.close();
				tmpFile.delete();

			}

			return folderAliases;
		} catch (CloudException ce) {
			throw ce;
		} catch (Throwable e) {
			throw new CloudException("Cannot retrieve folder aliases.",
					"ZECLD00002", e);
		}
	}

	/**
	 * Synchronize the folder alias list with the cloud.
	 * 
	 * @param cryptEngine
	 *            the crypting engine
	 * @param cloud
	 *            the cloud service to use
	 * @param folderAliases
	 *            the folder aliases
	 * @throws CloudException
	 *             if an error occurs
	 */
	public static void synchronizeFolderAliases(AESCrypt cryptEngine,
			Cloud cloud, List<FolderAlias> folderAliases) throws CloudException {
		try {
			List<FolderAlias> allAliases = new ArrayList<FolderAlias>(
					folderAliases);
			List<FolderAlias> aliasToCreate = new ArrayList<FolderAlias>();

			List<FolderAlias> remoteAliases = getFolderAliases(cryptEngine,
					cloud);
			// see if some remote folder does not exists in the provider
			// folderAliases
			for (FolderAlias remoteAlias : remoteAliases) {
				int index = allAliases.indexOf(remoteAlias);
				if (index == -1) {
					allAliases.add(remoteAlias);
				}
			}

			// see if we must create not alias on the cloud
			for (FolderAlias localAlias : allAliases) {
				if (!remoteAliases.contains(localAlias)) {
					aliasToCreate.add(localAlias);
				}
			}

			// create all new folders
			for (FolderAlias newAlias : aliasToCreate) {
				cloud.createFolder("/" + newAlias.getUid());
			}
			// create the index file
			File tmpFile = File.createTempFile("aliases", ".tmp");
			File tmpEncryptFile = File.createTempFile("aliases", ".crypt");
			BufferedWriter outputStream = new BufferedWriter(
					new OutputStreamWriter(new FileOutputStream(tmpFile),
							"utf-8"));

			for (FolderAlias alias : allAliases) {
				if (alias.getName() == null) {
					outputStream.write(alias.getUid() + "=");
				} else {
					outputStream.write(alias.getUid() + "=" + alias.getName());
				}
				outputStream.newLine();
			}
			outputStream.close();
			// crypt tmpfile
			cryptEngine.encrypt(2, tmpFile.getAbsolutePath(),
					tmpEncryptFile.getAbsolutePath());
			tmpFile.delete();
			// upload it to the cloud
			FileInputStream inputStream = new FileInputStream(tmpEncryptFile);
			// DbxEntry.File uploadedFile = client.uploadFile("/fmeo",
			// DbxWriteMode.force(), tmpFile.length(), inputStream);
			cloud.uploadFile("/aliases", tmpEncryptFile.length(), inputStream);
			inputStream.close();

			// delete temp file
			tmpEncryptFile.delete();
		} catch (CloudException ce) {
			throw ce;
		} catch (Throwable e) {
			throw new CloudException(
					"Cannot synchronize the folder alias list with the cloud repository.",
					"ZECLD00003", e);
		}
	}
}
